package elements.axis; 
import flash.display.Sprite;
import flash.geom.Matrix;
import string.Utils;
import charts.series.bars.Bar3D;
import hxjson2.JSON;
import Range;
import elements.Types.Style;

// TODO - check if "label['grid-colour']" is faulty

class XAxis extends Sprite {

	var steps:Int;
	var alt_axis_colour:Float;
	var alt_axis_step:Float;
	var three_d:Bool;
	var three_d_height:Int;
	
	var stroke:Int;
	var tick_height:Int;
	var colour:Float;
	public var offset:Bool;
	var grid_colour:Float;
	var user_ticks:Bool;
	var user_labels:Array<String>;
	
	// make this private
	public var labels:XAxisLabels;

	var style : Style;
	
	public function new( json:Dynamic, min:Float, max:Float )
	{
		super();
		// default values
		this.style= 
		{
				stroke:			2,
				tick_height:		3,
				colour:			'#784016',
				offset:			true,
				grid_colour:		'#F5E1AA',
				three_d:		false,
				steps:			1,
				min:			0,
				max:			-1
		};
		
		if( json != null )
			Object_helper.merge_2( json.x_axis, this.style );
		
		this.calcSteps();
		
		this.stroke = this.style.stroke;
		this.tick_height = this.style.tick_height;
		this.colour = this.style.colour;
		// is the axis offset (see ScreenCoords)
		this.offset = this.style.offset;
		this.grid_colour = this.style.grid_colour;

		this.colour = Utils.get_colour( this.style.colour );
		this.grid_colour = Utils.get_colour( this.style.grid_colour );
		
		
		var _3d_ = style['3d'] > 0;
		this.three_d = _3d_;
		if( _3d_ )
		{
			this.three_d_height = Std.parseInt( this.style['3d'] );
		}
		

		// Patch from Will Henry
		if( json )
		{
			if( json.x_label_style != undefined ) {
				if( json.x_label_style.alt_axis_step != undefined )
					this.alt_axis_step = json.x_label_style.alt_axis_step;
					
				if( json.x_label_style.alt_axis_colour != undefined )
					this.alt_axis_colour = Utils.get_colour(json.x_label_style.alt_axis_colour);
			}
		}
		
		this.labels = new XAxisLabels( json );
		this.addChild( this.labels );
					
		// the X Axis labels *may* require info from
		// this.obs
		if( !this.range_set() )
		{
			//
			// the user has not told us how long the X axis
			// is, so we figure it out:
			//
			if( this.labels.need_labels ) {
				//
				// No X Axis labels set:
				//
				
				// Tr.acesPortMe( 'max x', this.obs.get_min_x(), this.obs.get_max_x() );
				this.set_range( min, max );
			}
			else
			{
				//
				// X Axis labels used, even so, make the chart
				// big enough to show all values
				//
				// Tr.acesPortMe('x labels', this.obs.get_min_x(), this.x_axis.labels.count(), this.obs.get_max_x());
				if ( this.labels.count() > max ) {
					
					// Data and labesl are OK
					this.set_range( 0, this.labels.count() );
				} else {
					
					// There is more data than labels -- oops
					this.set_range( min, max );
				}
			}
		}
		else
		{
			//range set, but no labels...
			this.labels.auto_label( this.get_range(), this.get_steps() );
		}
		
		// custom ticks:
		this.make_user_ticks();
	}
	
	//
	// a little hacky, but we inspect the labels
	// to see if we need to display user custom ticks
	//
	function make_user_ticks():Void {		
// 		if (
// 			this.style.labels == null ||
// 			this.style.labels.labels == null ||
// 			!Std.is( this.style.labels.labels, Array<String>) ||
// 			this.style.labels.labels.length <= 0
// 		)
		if (this.style.labels == null) return;
		if (this.style.labels.labels == null) return;
		if (!Std.is(this.style.labels.labels, Array)) return; // TODO - report haxe error Array<String> doesn't parse with Std.is says unexpected parentheses
		if (this.style.labels.labels.length == 0) return;

		this.user_labels = new Array<String>();
		for ( label in this.style.labels.labels )
		{
			if (!Std.is(label, String) && label.x != null) {
				var l : { x : String , grid_colour : String } = { x: label.x };
				if (label["grid-colour"] != null)
				{
					l.grid_colour = Utils.get_colour(label["grid-colour"]);
				}
				else
				{
					l.grid_colour = this.grid_colour;
				}
				this.user_ticks = true;
				this.user_labels.push(l);
			}
		}
	}
	
	function calcSteps():Void {
		if (this.style.max == null) {
			this.steps = 1;
			return;
		}
		var xRange:Int = this.style.max - this.style.min;
		var rev:Bool = (this.style.min >= this.style.max); // min-max reversed?
		this.steps = ((this.style.steps != null) && 
									(this.style.steps != 0)) ? this.style.steps : 1;

		// force max of 250 labels and tick marks
		if ((Math.abs(xRange) / this.steps) > 250) this.steps = xRange / 250;

		// guarantee labelSteps is the proper sign
		this.steps = rev ? -Math.abs(this.steps) : Math.abs(this.steps);
	}

	//
	// have we been passed a range? (min and max?)
	//
	public function range_set():Bool {
		return this.style.max != null;
	}
	
	//
	// We don't have a range, so we need to calculate it.
	// grid lines, depends on number of values,
	// the X Axis labels and X min and X max
	//
	public function set_range( min:Float, max:Float ):Void
	{
		this.style.min = min;
		this.style.max = max;
		// Calc new steps
		this.calcSteps();
		
		this.labels.auto_label( this.get_range(), this.get_steps() );
	}
	
	//
	// how many items in the X axis?
	//
	public function get_range():Range {
		return new Range( this.style.min, this.style.max, this.steps, this.offset );
	}
	
	public function get_steps():Float {
		return this.steps;
	}
	
	public function resize( sc:ScreenCoords, yPos:Float ):Void
	{
		this.graphics.clear();
		
		//
		// Grid lines
		//
		if (this.user_ticks) 
		{
			for ( label in this.user_labels )
			{
				this.graphics.beginFill(label["grid-colour"], 1);
				var xVal:Int = sc.get_x_from_val(label.x);
				this.graphics.drawRect( xVal, sc.top, 1, sc.height );
				this.graphics.endFill();
			}
		}
		else
		{
			var rev:Bool = (this.style.min >= this.style.max); // min-max reversed?
			var tickMax:Int = /*(rev && this.style.offset) ? this.style.max-2 : */ this.style.max;
			
			var i:Int=this.style.min;
			while ( rev ? i >= tickMax : i <= tickMax)
			{
				if( ( this.alt_axis_step > 1 ) && ( i % this.alt_axis_step == 0 ) )
					this.graphics.beginFill(this.alt_axis_colour, 1);
				else
					this.graphics.beginFill(this.grid_colour, 1);
				
				xVal = sc.get_x_from_val(i);
				this.graphics.drawRect( xVal, sc.top, 1, sc.height );
				this.graphics.endFill();
				i+=this.steps ;
			}
		}
		
		if( this.three_d )
			this.three_d_axis( sc );
		else
			this.two_d_axis( sc );
		
		this.labels.resize( sc, yPos );
	}
		
	public function three_d_axis( sc:ScreenCoords ):Void
	{
		
		// for 3D
		var h:Int = this.three_d_height;
		var offset:Int = 12;
		var x_axis_height:Int = h+offset;
		
		//
		// ticks
		var item_width:Int = sc.width / this.style.max;
	
		// turn off out lines:
		this.graphics.lineStyle(0,0,0);
		
		var w:Int = 1;

		if (this.user_ticks) 
		{
			for ( label in this.user_labels )
			{
				var xVal:Int = sc.get_x_from_val(label.x);
				this.graphics.beginFill(this.colour, 1);
				this.graphics.drawRect( xVal, sc.bottom + x_axis_height, w, this.tick_height );
				this.graphics.endFill();
			}
		}
		else
		{
			var i:Int=0;
			while ( i < this.style.max)
			{
				var pos:Int = sc.get_x_tick_pos(i);
				
				this.graphics.beginFill(this.colour, 1);
				this.graphics.drawRect( pos, sc.bottom + x_axis_height, w, this.tick_height );
				this.graphics.endFill();
				i+=this.steps ;
			}
		}

		
		var lighter:Int = Bar3D.Lighten( this.colour );
		
		// TOP
		var colors:Array<String> = [this.colour,lighter];
		var alphas:Array<Int> = [100,100];
		var ratios:Array<Int> = [0,255];
	
		var matrix:Matrix = new Matrix();
		matrix.createGradientBox(sc.width_(), offset, (270 / 180) * Math.PI, sc.left-offset, sc.bottom );
		this.graphics.beginGradientFill('linear' /*GradientType.Linear*/, colors, alphas, ratios, matrix, 'pad'/*SpreadMethod.PAD*/ );
		this.graphics.moveTo(sc.left,sc.bottom);
		this.graphics.lineTo(sc.right,sc.bottom);
		this.graphics.lineTo(sc.right-offset,sc.bottom+offset);
		this.graphics.lineTo(sc.left-offset,sc.bottom+offset);
		this.graphics.endFill();
	
		// front
		colors = [this.colour,lighter];
		alphas = [100,100];
		ratios = [0, 255];
		
		matrix.createGradientBox( sc.width_(), h, (270 / 180) * Math.PI, sc.left - offset, sc.bottom + offset );
		this.graphics.beginGradientFill("linear", colors, alphas, ratios, matrix);
		this.graphics.moveTo(sc.left-offset,sc.bottom+offset);
		this.graphics.lineTo(sc.right-offset,sc.bottom+offset);
		this.graphics.lineTo(sc.right-offset,sc.bottom+offset+h);
		this.graphics.lineTo(sc.left-offset,sc.bottom+offset+h);
		this.graphics.endFill();
		
		// right side
		colors = [this.colour,lighter];
		alphas = [100,100];
		ratios = [0,255];
		
	//	var matrix:Object = { matrixType:"box", x:box.left - offset, y:box.bottom + offset, w:box.width_(), h:h, r:(225 / 180) * Math.PI };
		matrix.createGradientBox( sc.width_(), h, (225 / 180) * Math.PI, sc.left - offset, sc.bottom + offset );
		this.graphics.beginGradientFill("linear", colors, alphas, ratios, matrix);
		this.graphics.moveTo(sc.right,sc.bottom);
		this.graphics.lineTo(sc.right,sc.bottom+h);
		this.graphics.lineTo(sc.right-offset,sc.bottom+offset+h);
		this.graphics.lineTo(sc.right-offset,sc.bottom+offset);
		this.graphics.endFill();
		
	}
	
	// 2D:
	public function two_d_axis( sc:ScreenCoords ):Void
	{
		//
		// ticks
		var item_width:Int = sc.width / this.style.max;
		var left:Int = sc.left+(item_width/2);
	
		//this.graphics.clear();
		// Axis line:
		this.graphics.lineStyle( 0, 0, 0 );
		this.graphics.beginFill( this.colour );
		this.graphics.drawRect( sc.left, sc.bottom, sc.width, this.stroke );
		this.graphics.endFill();
		
		
		if (this.user_ticks) 
		{
			for ( label in this.user_labels )
			{
				var xVal:Int = sc.get_x_from_val(label.x);
				this.graphics.beginFill(this.colour, 1);
				this.graphics.drawRect( xVal-(this.stroke/2), sc.bottom + this.stroke, this.stroke, this.tick_height );
				this.graphics.endFill();
			}
		}
		else
		{
			var i:Int=this.style.min;
			while ( i <= this.style.max)
			{
				xVal = sc.get_x_from_val(i);
				this.graphics.beginFill(this.colour, 1);
				this.graphics.drawRect( xVal-(this.stroke/2), sc.bottom + this.stroke, this.stroke, this.tick_height );
				this.graphics.endFill();
				i+=this.steps ;
			}
		}
	}
	
	public function height_():Float {
		return this.stroke + this.tick_height;
	}
	
	public function get_height():Float {
		if( this.three_d )
		{
			// 12 is the size of the slanty
			// 3D part of the X axis
			return this.three_d_height+12+this.tick_height;
		}
		else
			return this.stroke + this.tick_height + this.labels.get_height();
	}
	
	public function first_label_width() : Float
	{
		return this.labels.first_label_width();
	}
	
	public function last_label_width() : Float
	{
		return this.labels.last_label_width();
	}
	
	public function die(): Void {
		
		this.style = null;
	
		this.graphics.clear();
		while ( this.numChildren > 0 )
			this.removeChildAt(0);
		
		if (this.labels != null)
			this.labels.die();
		this.labels = null;
	}
}
